home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 19 / CU Amiga Magazine's Super CD-ROM 19 (1998)(EMAP Images)(GB)[!][issue 1998-02].iso / CUCD / Graphics / jpegoptim / src / jpegoptim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-22  |  14.2 KB  |  624 lines

  1. /*******************************************************************
  2.  * JPEGoptim
  3.  * Copyright (c) Timo Kokkonen, 1996.
  4.  *
  5.  * requires libjpeg.a (from JPEG Group's JPEG software 
  6.  *                     release 6a or later...)
  7.  *
  8.  * to compile type: gcc -o jpegoptim jpegoptim.c -ljpeg
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/param.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <unistd.h>
  17. #include <dirent.h>
  18. #ifndef HPUX
  19. #ifndef AMIGA
  20. #include <getopt.h>
  21. #endif
  22. #endif
  23. #include <signal.h>
  24. #include <string.h>
  25. #include <jpeglib.h>
  26. #include <setjmp.h>
  27. #ifdef SGI
  28. #include <libgen.h>
  29. #endif
  30.  
  31. #define VERSIO "1.1"
  32.  
  33. #ifdef AMIGA
  34. #define TEMPDIR "t:"
  35. #define TEMP2DIR "t:"
  36. #else
  37. #define TEMPDIR "."
  38. #define TEMP2DIR "/tmp"
  39. #endif
  40.  
  41. #ifdef SGI
  42. #undef METHODDEF
  43. #define METHODDEF(x) static x
  44. #endif
  45.  
  46.  
  47. struct my_error_mgr {
  48.   struct jpeg_error_mgr pub;
  49.   jmp_buf setjmp_buffer;   
  50. };
  51.  
  52. typedef struct my_error_mgr * my_error_ptr;
  53.  
  54. struct jpeg_decompress_struct dinfo;
  55. struct jpeg_compress_struct cinfo;
  56. struct my_error_mgr jcerr,jderr;
  57.  
  58. #ifdef LONG_OPTIONS
  59. struct option long_options[] = {
  60.   {"verbose",0,0,'v'},
  61.   {"help",0,0,'h'},
  62.   {"quiet",0,0,'q'},
  63.   {"max",1,0,'m'},
  64.   {"totals",0,0,'t'},
  65.   {"noaction",0,0,'n'},
  66.   {"dest",1,0,'d'},
  67.   {"force",0,0,'f'},
  68.   {0,0,0,0}
  69. };
  70. #endif
  71.  
  72. int verbose_mode = 0;
  73. int quiet_mode = 0;
  74. int global_error_counter = 0;
  75. char *outfname = NULL;
  76. FILE *infile = NULL, *outfile = NULL;
  77.  
  78. #ifdef AMIGA
  79. const char dummie[]="$VER: jpegoptim " VERSIO ;
  80. #endif
  81.  
  82. /*****************************************************************/
  83.  
  84. METHODDEF(void) 
  85. my_error_exit (j_common_ptr cinfo)
  86. {
  87.   my_error_ptr myerr = (my_error_ptr)cinfo->err;
  88.   (*cinfo->err->output_message) (cinfo);
  89.   longjmp(myerr->setjmp_buffer,1);
  90. }
  91.  
  92. METHODDEF(void)
  93. my_output_message (j_common_ptr cinfo)
  94. {
  95.   char buffer[JMSG_LENGTH_MAX];
  96.  
  97.   (*cinfo->err->format_message) (cinfo, buffer); 
  98.   printf(" %s ",buffer);
  99.   global_error_counter++;
  100. }
  101.     
  102. #ifdef AMIGA /* some sucking replacement routines */
  103. int crap_rename(char *vanha, char *uusi)
  104. {
  105.     unsigned char *buffer;
  106.     FILE *fp;
  107.     FILE *dp;
  108.     unsigned long luettu=0;
  109.     int c;
  110.  
  111.     if((buffer=malloc(32000)))
  112.     {
  113.       if((fp=fopen(vanha, "rb")))
  114.       {
  115.          if((dp=fopen(uusi, "wb")))
  116.          {
  117.             while((c=fgetc(fp))!=EOF)            
  118.             {                                    
  119.                 ungetc(c, fp);               
  120.                 luettu=fread(buffer, 1, 32000, fp);
  121.                 fwrite(buffer,1,luettu,dp);
  122.             }
  123.             fclose(dp);
  124.          }
  125.          else 
  126.          {
  127.             printf("Error opening %s for writing.\n", uusi);
  128.             return(1);
  129.          }
  130.          fclose(fp);
  131.       }
  132.       else 
  133.       { 
  134.          printf("Error opening %s for reading.\n", vanha);
  135.          return(1);
  136.       }
  137.       free(buffer);
  138.     }
  139.     else 
  140.     {
  141.       printf("Can't allocate 32000 bytes of mem.\n");
  142.       return(1);
  143.     }
  144.  
  145.     unlink(vanha);
  146.  
  147.     return(0);
  148. }
  149.  
  150. char *realpath __P((const char *jorma, char *keijo))
  151. {
  152.   int c;
  153.  
  154.   if(!jorma) return(NULL);
  155.  
  156.   strcpy(keijo, jorma);
  157.  
  158.   c=strlen(keijo)-1;
  159.  
  160.   if(keijo[c]!=':' && keijo[c]!='/')
  161.   {
  162.     keijo[c+1]='/';
  163.      keijo[c+2]=NULL;
  164.   }
  165.  
  166.   return(keijo);
  167. }
  168.  
  169. char *basename(char *polku)
  170. {
  171.   char *temppi;
  172.  
  173.   while((temppi=strchr(polku,':')))
  174.   {
  175.     polku=temppi+1;
  176.   }
  177.  
  178.   while((temppi=strchr(polku,'/')))
  179.   {
  180.     polku=temppi+1;
  181.   }
  182.  
  183.   return(polku);
  184. }
  185. #endif
  186.  
  187. void no_memory(void)
  188. {
  189.   if (!quiet_mode) fprintf(stderr,"jpegoptim: not enough memory.\n");
  190.   exit(3);
  191. }
  192.  
  193. void p_usage(void) 
  194. {
  195.  if (!quiet_mode) {
  196.   fprintf(stderr,"jpegoptim " VERSIO 
  197.       " Copyright (c) Timo Kokkonen, 1996.\n"); 
  198.  
  199.   fprintf(stderr,
  200.        "Usage: jpegoptim [options] <filenames> \n\n"
  201. #ifdef LONG_OPTIONS
  202.        "  -d<path>, --dest=<path>\n"
  203.        "                 specify alternative destination directory for \n"
  204.        "                 optimized files (default is to overwrite originals)\n"
  205.        "  -f, --force    force optimization\n"
  206.        "  -h, --help     display this help and exit\n"
  207.        "  -m[0..100], --max=[0..100] \n"
  208.        "                 set maximum image quality factor (disables lossless\n"
  209.        "                 optimization mode, which is by default on)\n"
  210.        "  -n, --noaction don't really optimize files, just print results\n"
  211.        "  -q, --quiet    quiet mode\n"
  212.        "  -t, --totals   print totals after processing all files\n"
  213.        "  -v, --verbose  enable verbose mode (positively chatty)\n"
  214. #else
  215.        "  -d<path>       specify alternative destination directory for \n"
  216.        "                 optimized files (default is to overwrite originals)\n"
  217.        "  -f             force optimization\n"
  218.        "  -h             display this help and exit\n"
  219.        "  -m[0..100]     set maximum image quality factor (disables lossless\n"
  220.        "                 optimization mode, which is by default on)\n"
  221.        "  -n             don't really optimize files, just print results\n"
  222.        "  -q             quiet mode\n"
  223.        "  -t             print totals after processing all files\n"
  224.        "  -v             enable verbose mode (positively chatty)\n"
  225. #endif
  226.        "\n\n");
  227.  }
  228.  
  229.  exit(1);
  230. }
  231.  
  232. int delete_file(char *name)
  233. {
  234.   int retval;
  235.  
  236.   if (verbose_mode&&!quiet_mode) fprintf(stderr,"deleting: %s\n",name);
  237.   if ((retval=unlink(name))&&!quiet_mode) {
  238.     fprintf(stderr,"jpegoptim: error removing file: %s\n",name);
  239.   }
  240.  
  241.   return retval;
  242. }
  243.  
  244. int file_size(FILE *fp)
  245. {
  246.   long size=0,save=0;
  247.   
  248.   fgetpos(fp,&save);
  249.   fseek(fp,0L,SEEK_END);
  250.   fgetpos(fp,&size);
  251.   fseek(fp,save,SEEK_SET);
  252.   
  253.   return size;
  254. }
  255.  
  256. int is_directory(const char *path)
  257. {
  258.   DIR *dir = opendir(path);
  259.  
  260.   if (!dir) {
  261.     return 1;
  262.   }
  263.   closedir(dir);
  264.   return 0;
  265. }
  266.  
  267.  
  268. int is_dir(FILE *fp)
  269. {
  270.  struct stat buf;
  271.  if (fstat(fileno(fp),&buf)) {
  272.    fprintf(stderr,"jpeginfo: fstat() failed.\n");
  273.    exit(3);
  274.  }
  275.  
  276.  if (S_ISDIR(buf.st_mode)) return 1;
  277.  
  278.  return 0;
  279. }
  280.  
  281.  
  282. int file_exists(const char *pathname)
  283. {
  284.   FILE *file=fopen(pathname,"r");
  285.   
  286.   if (!file) return 0;
  287.   fclose(file);
  288.   return 1;
  289. }
  290.  
  291. void own_signal_handler(int a)
  292. {
  293.   if (verbose_mode) printf("jpegoptim: signal: %d\n",a);
  294.   if (outfile) fclose(outfile);
  295.   if (outfname) if (file_exists(outfname)) delete_file(outfname);
  296.   exit(1);
  297. }
  298.  
  299. /*****************************************************************/
  300. int main(int argc, char **argv) 
  301. {
  302.   JSAMPARRAY buf = NULL;
  303.   char name1[MAXPATHLEN],name2[MAXPATHLEN];
  304.   char newname[MAXPATHLEN], dest_path[MAXPATHLEN];
  305.   jvirt_barray_ptr *coef_arrays=NULL;
  306.   int c,i,j,lines_read, err_count;
  307.   int totals_mode = 0;
  308.   int opt_index = 0;
  309.   int quality = -1;
  310.   int noaction = 0;
  311.   int retry = 0;
  312.   int dest = 0;
  313.   int force = 0;
  314.   long insize,outsize,average_count=0;
  315.   double ratio,average_rate = 0.0,total_save=0.0;
  316.   pid_t cur_pid = getpid();
  317.   uid_t cur_uid = getuid();
  318.  
  319. #ifdef AMIGA
  320.   sprintf(name1,TEMPDIR "jpegoptim.%06x.%04x.tmp",cur_pid,cur_uid);
  321.   sprintf(name2,TEMP2DIR "jpegopti2.%06x.%04x.tmp",cur_pid,cur_uid);
  322. #else
  323.   sprintf(name1,TEMPDIR "/jpegoptim.%06x.%04x.tmp",cur_pid,cur_uid);
  324.   sprintf(name2,TEMP2DIR "/jpegoptim.%06x.%04x.tmp",cur_pid,cur_uid);
  325. #endif
  326.  
  327.   signal(SIGINT,own_signal_handler);
  328.   signal(SIGTERM,own_signal_handler);
  329.  
  330.   /* initialize decompression object */
  331.   dinfo.err = jpeg_std_error(&jderr.pub);
  332.   jpeg_create_decompress(&dinfo);
  333.   jderr.pub.error_exit=my_error_exit;
  334.   jderr.pub.output_message=my_output_message;
  335.  
  336.   /* initialize compression object */
  337.   cinfo.err = jpeg_std_error(&jcerr.pub);
  338.   jpeg_create_compress(&cinfo);
  339.   jcerr.pub.error_exit=my_error_exit;
  340.   jcerr.pub.output_message=my_output_message;
  341.  
  342.  
  343.  
  344.   if (argc<2) {
  345.     if (!quiet_mode) fprintf(stderr,"jpegoptim: file arguments missing\n"
  346.                  "Try 'jpegoptim "
  347. #ifdef LONG_OPTIONS
  348.                  "--help"
  349. #else
  350.                  "-h"
  351. #endif
  352.                  "' for more information.\n");
  353.     exit(1);
  354.   }
  355.  
  356.   /* parse command line parameters */
  357.   while(1) {
  358.     opt_index=0;
  359. #ifdef LONG_OPTIONS
  360.     if ((c=getopt_long(argc,argv,"d:hm:ntqvf",long_options,&opt_index))==-1) 
  361. #else
  362.     if ((c=getopt(argc,argv,"d:hm:ntqvf"))==-1) 
  363. #endif
  364.       break;
  365.     switch (c) {
  366.     case 'm':
  367.       {
  368.         int tmpvar;
  369.  
  370.         if (sscanf(optarg,"%d",&tmpvar) == 1) {
  371.       quality=tmpvar;
  372.       if (quality < 0) quality=0;
  373.       if (quality > 100) quality=100;
  374.     }
  375.     else if (!quiet_mode) {
  376.       fprintf(stderr,"jpegoptim: invalid argument for -m, --max\n");
  377.       exit(1);
  378.     }
  379.       }
  380.       break;
  381.     case 'd':
  382.       if (realpath(optarg,dest_path)==NULL || is_directory(dest_path)) {
  383.     fprintf(stderr,"jpegoptim: invalid argument for -d, --dest\n");
  384.     exit(1);
  385.       }
  386.       if (verbose_mode) 
  387.     fprintf(stderr,"Destination directory: %s\n",dest_path);
  388.       dest=1;
  389.       break;
  390.     case 'v':
  391.       verbose_mode=1;
  392.       break;
  393.     case 'h':
  394.       p_usage();
  395.       break;
  396.     case 'q':
  397.       quiet_mode=1;
  398.       break;
  399.     case 't':
  400.       totals_mode=1;
  401.       break;
  402.     case 'n':
  403.       noaction=1;
  404.       break;
  405.     case 'f':
  406.       force=1;
  407.       break;
  408.     case '?':
  409.       break;
  410.  
  411.     default:
  412.       if (!quiet_mode) 
  413.     fprintf(stderr,"jpegoptim: error parsing parameters.\n");
  414.     }
  415.   }
  416.  
  417.  
  418.   if (verbose_mode && (quality>0)) 
  419.     fprintf(stderr,"Image quality limit set to: %d\n",quality);
  420.  
  421.  
  422.   /* loop to process the input files */
  423.   i=1;  
  424.   do {
  425.  
  426.    if (!argv[i][0]) continue;
  427.    if (argv[i][0]=='-') continue;
  428.  
  429.   retry_point:
  430.    if ((infile=fopen(argv[i],"r"))==NULL) {
  431.      if (!quiet_mode) fprintf(stderr, "jpegoptim: can't open %s\n", argv[i]);
  432.      continue;
  433.    }
  434.    if (is_dir(infile)) {
  435.      fclose(infile);
  436.      if (verbose_mode) printf("directory: %s  skipped\n",argv[i]); 
  437.      continue;
  438.    }
  439.  
  440.    /* setup error handling for decompress */
  441.    if (setjmp(jderr.setjmp_buffer)) {
  442.       jpeg_abort_decompress(&dinfo);
  443.       fclose(infile);
  444.       printf(" [ERROR]\n");
  445.       continue;
  446.    }
  447.  
  448.    if (!retry) { printf("%s ",argv[i]); fflush(stdout); }
  449.  
  450.    /* prepare to decompress */
  451.    global_error_counter=0;
  452.    err_count=jderr.pub.num_warnings;
  453.    jpeg_stdio_src(&dinfo, infile);
  454.    jpeg_read_header(&dinfo, TRUE); 
  455.  
  456.    if (!retry) {
  457.      printf("%dx%d %dbit ",(int)dinfo.image_width,
  458.         (int)dinfo.image_height,(int)dinfo.num_components*8);
  459.      if (dinfo.saw_Adobe_marker) printf("Adobe ");
  460.      else if (dinfo.saw_JFIF_marker) printf("JFIF ");
  461.      else printf("Unknown ");
  462.      fflush(stdout);
  463.    }
  464.  
  465.    insize=file_size(infile);
  466.  
  467.   /* decompress the file */
  468.    if (quality>=0 && !retry) {
  469.      jpeg_start_decompress(&dinfo);
  470.  
  471.      buf = malloc(sizeof(JSAMPROW)*dinfo.output_height);
  472.      if (!buf) no_memory();
  473.      for (j=0;j<dinfo.output_height;j++) {
  474.        buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width*
  475.              dinfo.out_color_components);
  476.        if (!buf[j]) no_memory();
  477.      }
  478.  
  479.      while (dinfo.output_scanline < dinfo.output_height) {
  480.        jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
  481.                dinfo.output_height-dinfo.output_scanline);
  482.      }
  483.    } else {
  484.      coef_arrays = jpeg_read_coefficients(&dinfo);
  485.    }
  486.  
  487.    if (!retry) {
  488.      if (!global_error_counter) printf(" [OK] ");
  489.      else {
  490.        printf(" [WARNING] defective file, skipped.\n");
  491.        jpeg_abort_decompress(&dinfo);
  492.        fclose(infile);
  493.        for (j=0;j<dinfo.output_height;j++) free(buf[j]);
  494.        free(buf); buf=NULL;
  495.        outfname=NULL;
  496.        continue;
  497.      }
  498.        
  499.      fflush(stdout);
  500.    }
  501.  
  502.    outfname=(noaction ? name2 : name1);
  503.  
  504.    if (dest && !noaction) {
  505.      strcpy(newname,dest_path);
  506. #ifndef AMIGA
  507.      strcat(newname,"/"); 
  508. #endif
  509.      strcat(newname,(char*)basename(argv[i]));
  510.      if (file_exists(newname)) {
  511.        fprintf(stderr,"jpegoptim: target file already exists: %s\n",
  512.            newname);
  513.        jpeg_abort_decompress(&dinfo);
  514.        fclose(infile);
  515.        for (j=0;j<dinfo.output_height;j++) free(buf[j]);
  516.        free(buf); buf=NULL;
  517.        outfname=NULL;
  518.        continue;
  519.      }
  520.      outfname=newname;
  521.    }
  522.  
  523.    if ((outfile=fopen(outfname,"w"))==NULL) {
  524.      if (!quiet_mode) fprintf(stderr,"\njpegoptim: error creating "
  525.                   "file: %s\n", outfname);
  526.      exit(1);
  527.    }
  528.  
  529.  
  530.    if (setjmp(jcerr.setjmp_buffer)) {
  531.       jpeg_abort_compress(&cinfo);
  532.       jpeg_abort_decompress(&dinfo);
  533.       fclose(outfile);
  534.       if (infile) fclose(infile);
  535.       printf(" [Compress ERROR]\n");
  536.       for (j=0;j<dinfo.output_height;j++) free(buf[j]);
  537.       free(buf); buf=NULL;
  538.       if (file_exists(outfname)) delete_file(outfname);
  539.       outfname=NULL;
  540.       continue;
  541.    }
  542.  
  543.    jpeg_stdio_dest(&cinfo, outfile);
  544.  
  545.    if (quality>=0 && !retry) {
  546.      cinfo.in_color_space=dinfo.out_color_space;
  547.      cinfo.input_components=dinfo.output_components;
  548.      cinfo.image_width=dinfo.image_width;
  549.      cinfo.image_height=dinfo.image_height;
  550.      jpeg_set_defaults(&cinfo); 
  551.      jpeg_set_quality(&cinfo,quality,TRUE);
  552.      cinfo.optimize_coding = TRUE;
  553.      
  554.      j=0;
  555.      jpeg_start_compress(&cinfo,TRUE);
  556.      while (cinfo.next_scanline < cinfo.image_height) {
  557.        jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
  558.                 dinfo.output_height);
  559.      }
  560.      
  561.      for (j=0;j<dinfo.output_height;j++) free(buf[j]);
  562.      free(buf); buf=NULL;
  563.    } else {
  564.      jpeg_copy_critical_parameters(&dinfo, &cinfo);
  565.      cinfo.optimize_coding = TRUE;
  566.      jpeg_write_coefficients(&cinfo, coef_arrays);
  567.    }
  568.  
  569.    jpeg_finish_compress(&cinfo);
  570.    jpeg_finish_decompress(&dinfo);
  571.    fclose(infile);
  572.    outsize=file_size(outfile);
  573.    fclose(outfile);
  574.  
  575.    if (quality>=0 && outsize>=insize && !retry) {
  576.      if (verbose_mode) printf("(retry w/lossless) ");
  577.      retry=1;
  578.      goto retry_point; 
  579.    }
  580.  
  581.    retry=0;
  582.    ratio=(insize-outsize)*100.0/insize;
  583.    printf("%d --> %d bytes (%0.2lf%%), ",insize,outsize,ratio);
  584.    average_count++;
  585.    average_rate+=(ratio<0 ? 0.0 : ratio);
  586.  
  587.    if (outsize<insize || force) {
  588.         total_save+=(insize-outsize)/1024.0;
  589.     printf("optimized.\n");
  590.         if (noaction || dest) { continue; }
  591.     if (!delete_file(argv[i])) {
  592.         if (verbose_mode&&!quiet_mode) 
  593.           fprintf(stderr,"Renaming: %s to %s\n",outfname,argv[i]);
  594. #ifdef AMIGA
  595.         if (crap_rename(outfname,argv[i])) {
  596. #else
  597.         if (rename(outfname,argv[i])) {
  598. #endif
  599.           fprintf(stderr,"Cannot rename temp file.\n");
  600.               exit(3);
  601.         }
  602.     }
  603.     
  604.    } else {
  605.     printf("skipped.\n");
  606.     if (!noaction) delete_file(outfname);
  607.    }
  608.  
  609.   } while (++i<argc);
  610.  
  611.   if (noaction && file_exists(outfname)) delete_file(outfname);
  612.   if (totals_mode&&!quiet_mode)
  613.     printf("Average ""compression"" (%ld files): %0.2lf%% (%0.0lfk)\n",
  614.        average_count, average_rate/average_count, total_save);
  615.  
  616.   jpeg_destroy_decompress(&dinfo);
  617.   jpeg_destroy_compress(&cinfo);
  618.  
  619.  
  620.   return 0;
  621. }
  622.  
  623. /* :-) */
  624.